/*
 * Client.cpp
 *
 *  Created on: 27 f�vr. 2013
 *      Author: Aurel
 */

#include "Network/Client.hpp"

Client::Client(GameEngine* p_gameEngine) : m_gameEngine(p_gameEngine), m_Selector() , m_ServerAddress(), m_Socket() {

	this->m_isRunning = true;
}
Client::~Client() {
	this->Stop();
}
/**
 * Start the client and try to connect the server.
 * if false the client is not connected and he doesn't start the thread
 *\/!\ Depreciated
 */
bool Client::StartClient()
{
	return this->StartClient(sf::IPAddress::LocalHost.ToString(), "Player");
}
/**
 * Start the client and try to connect the server.
 * if false the client is not connected and he doesn't start the thread
 * use a name and a Ip defined in parameter
 */
bool Client::StartClient(std::string p_PlayerName, std::string p_IPAddress)
{

	m_ServerAddress = sf::IPAddress(p_IPAddress);

	Debug::printTrace(Debug::information,"Network/Client.Connexion => IP", p_IPAddress);
	Debug::printTrace(Debug::information,"Network/Client.Connexion => IP", m_ServerAddress.ToString());
	Debug::printTrace(Debug::information,"Network/Client.Connexion => name", p_PlayerName);

	if(m_ServerAddress.IsValid())
	{
		Debug::printTrace(Debug::information,"Network/Client.Connexion", "try connexion");

		if (m_Socket.Connect(DefaultValue::NETWORK_PORT, m_ServerAddress) != sf::Socket::Done)
		{
			Debug::printTrace(Debug::information,"Network/Client.Constructor", "Connection impossible");
		}
		else
		{
			m_Selector.Add(m_Socket);

			if(this->Connection(p_PlayerName) != 0)
			{
				this->Launch();
				return true;
			}
		}
	}
	this->m_isRunning = false;
	return false;
}
/**
 * Stop Thread Client
 */
void Client::Stop()
{
	this->m_isRunning = false;
	this->SendDeconnexion();

	m_Socket.Close();

	this->Wait();
}
/**
 * Main loop : send the messages to the server
 */
void Client::Run()
{
	while(m_isRunning)
	{
		sf::Packet l_Packet;

		if (this->ReceivePacket(&l_Packet))
		{
			sf::Packet l_PacketMessage = l_Packet;
			int code;

			l_Packet >> code;

			Debug::printTrace(Debug::information,"Network/Client.Run => receive code =>", Text::float2string(code));

			switch (code) {
				case NetworkValues::NETWORK_CODE_NEW_PLAYER:
					this->NotifyNewPlayer(&l_PacketMessage);
					break;
				case NetworkValues::NETWORK_CODE_SPAWN_PLAYER:
					this->NotifyPlayerSpawn(&l_PacketMessage);
					break;
				case NetworkValues::NETWORK_CODE_DIRECTION_PLAYER:
					this->NotifyPlayerDirection(&l_PacketMessage);
					break;
				case NetworkValues::NETWORK_CODE_SHOOT:
					this->NotifyNewShoot(&l_PacketMessage);
					break;
				case NetworkValues::NETWORK_CODE_PLAYER_HIT:
					this->NotifyPlayerHit(&l_PacketMessage);
					break;
				case NetworkValues::NETWORK_CODE_PLAYER_KILL:
					this->NotifyPlayerKill(&l_PacketMessage);
					break;
				case NetworkValues::NETWORK_CODE_PLAYER_DIE:
					this->NotifyPlayerDeath(&l_PacketMessage);
					break;
				case NetworkValues::NETWORK_CODE_DECONNEXION:
					this->NotifyPlayerLeave(&l_PacketMessage);
					break;
				default:
					break;
			}
		}
	}
}
/**
 * Receipt a packet. wait during a timeout if necessary or until recept somthing.
 * return true if a packet is ok.
 */
bool Client::ReceivePacket (sf::Packet* p_packet , float p_timeOut)
{

	unsigned int l_NbSockets = 0;

	l_NbSockets = m_Selector.Wait(p_timeOut);

	//Debug::printTrace(Debug::information,"Network/Client.Receiver packet ?", Text::float2string(l_NbSockets));

	if(l_NbSockets > 0)
	{
		if (m_Socket.Receive(*p_packet) == sf::Socket::Done)
		{
			return true;
		}
	}
	return false;
}
/**
 * Send a connection packet to join a game.
 * Create the player if necessary.
 * return an id for the player. if id is null connection has been refused.
 */
int Client::Connection(const string& p_name)
{
	int l_code = 0;
	int l_netCode = NetworkValues::NETWORK_CODE_CONNECTION;

	sf::Packet l_packet;
	l_packet << l_netCode << p_name;
	m_Socket.Send(l_packet);

	Debug::printTrace(Debug::information,"Network/Client.Loop", "Connection authorization");

	if (this->ReceivePacket(&l_packet, NetworkValues::NETWORK_TIMEOUT))
	{
		l_packet >> l_netCode >> l_code;
		m_gameEngine->AddPlayer(l_code, p_name, Character::WAITING,
				0 , 				0,
				Character::None, 	Character::None,
				0, 					0);
	}
	//Debug::printTrace(Debug::information,"Network/Client.Loop", "Waiting finish");

	return l_code;
}
/**
 * Send the new direction for the player to the server
 */
void Client::SendNewDirection(int p_DirectionV, int p_DirectionH)
{
	sf::Packet l_packet;

	l_packet << NetworkValues::NETWORK_CODE_DIRECTION_PLAYER << p_DirectionH << p_DirectionV;
	m_Socket.Send(l_packet);

	/*Debug::printTrace(Debug::information,"Network/Client.SendNewDirection Vertical ", Text::float2string(p_DirectionV));
	Debug::printTrace(Debug::information,"Network/Client.SendNewDirection Horizont ", Text::float2string(p_DirectionH));*/

}
/**
 * Say "I want shoot my friend" to server
 */
void Client::SendNewShot(float p_DirectionH, float p_DirectionV)
{
	sf::Packet l_packet;

	l_packet << NetworkValues::NETWORK_CODE_SHOOT << p_DirectionH << p_DirectionV;
	m_Socket.Send(l_packet);
}
/**
 * Say Bye Bye to server
 */
void Client::SendDeconnexion()
{
	sf::Packet l_packet;

	l_packet << NetworkValues::NETWORK_CODE_DECONNEXION;
	m_Socket.Send(l_packet);
}
/**
 * Be notified by the server that a player has joined the game
 */
void Client::NotifyNewPlayer(sf::Packet* p_packet)
{
	int 					ms_code, ms_id, 	ms_Kill, ms_Death;
	float 					ms_life, ms_posX, 	ms_posY;
	Character::Direction  	ms_dirH, ms_dirV;
	Character::States 		ms_state;
	string 					ms_name;

	Debug::printTrace(Debug::information,"Network/Client.NotifyNewPlayer", "receive a new player");

	(*p_packet)	>> ms_code
				>> ms_name
				>> ms_id
				>> ms_life
				>> ms_posX
				>> ms_posY
				>> ms_dirV
				>> ms_dirH
				>> ms_state
				>> ms_Kill
				>> ms_Death;

	/*Debug::printTrace(Debug::information,"Network/Client.NotifyNewPlayer => value", "Waiting finish");
	Debug::printTrace(Debug::information,"Network/Client.NotifyNewPlayer => value", ms_name);
	Debug::printTrace(Debug::information,"Network/Client.NotifyNewPlayer => value", Text::float2string(ms_id));
	Debug::printTrace(Debug::information,"Network/Client.NotifyNewPlayer => value", Text::float2string(ms_life));
	Debug::printTrace(Debug::information,"Network/Client.NotifyNewPlayer => value", Text::float2string(ms_posX));
	Debug::printTrace(Debug::information,"Network/Client.NotifyNewPlayer => value", Text::float2string(ms_posY));
	Debug::printTrace(Debug::information,"Network/Client.NotifyNewPlayer => value", Text::float2string(ms_dirV));
	Debug::printTrace(Debug::information,"Network/Client.NotifyNewPlayer => value", Text::float2string(ms_Kill));
	Debug::printTrace(Debug::information,"Network/Client.NotifyNewPlayer => value", Text::float2string(ms_Death));
	 */
	this->m_gameEngine->AddPlayer(ms_id, ms_name, ms_state, ms_posX, ms_posY, ms_dirV, ms_dirH, ms_Kill, ms_Death);
}

/**
 * Be notified by the server that a player has respawn
 */
void Client::NotifyPlayerSpawn(sf::Packet* p_packet)
{
	int 					ms_code, ms_id;
	float 					ms_posX, 	ms_posY;

	(*p_packet)	>> ms_code
				>> ms_id
				>> ms_posX
				>> ms_posY;

	Debug::printTrace(Debug::information,"Network/Client.NotifySpawnPlayer =>", Text::float2string(ms_id));

	Character* l_character = this->m_gameEngine->GetPlayerById(ms_id);
	if(l_character != 0){
		l_character->Spawn(sf::Vector2f(ms_posX, ms_posY));
	}
}

/**
 * Be notified by the server that a player has a new directon
 */
void Client::NotifyPlayerDirection(sf::Packet* p_packet)
{
	int 					ms_code, 		ms_id;
	float 					ms_posX, 		ms_posY;
	Character::Direction	ms_directionV,	ms_directionH;

	(*p_packet)	>> ms_code
				>> ms_id
				>> ms_posX
				>> ms_posY
				>> ms_directionH
				>> ms_directionV;

	/*Debug::printTrace(Debug::information,"Network/Client.NotifyPlayerDirecton => playerId", Text::float2string(ms_id));
	Debug::printTrace(Debug::information,"Network/Client.NotifyPlayerDirecton => pos X", Text::float2string(ms_posX));
	Debug::printTrace(Debug::information,"Network/Client.NotifyPlayerDirecton => pos Y", Text::float2string(ms_posY));
	Debug::printTrace(Debug::information,"Network/Client.NotifyPlayerDirecton => dire H", Text::float2string(ms_directionH));
	Debug::printTrace(Debug::information,"Network/Client.NotifyPlayerDirecton => dire V", Text::float2string(ms_directionV));
*/

	Character* l_character = this->m_gameEngine->GetPlayerById(ms_id);

	if(l_character == 0)
		Debug::printTrace(Debug::critical,"Network/Client.NotifyPlayerDirecton", "NoPlayer with this ID");

	l_character->SetDirectionHorizontal	(ms_directionH);
	l_character->SetDirectionVertical	(ms_directionV);
	l_character->Setposition			(sf::Vector2f(ms_posX, ms_posY));
}
/**
 * Be notified by the server that a player has shouted
 */
void Client::NotifyNewShoot(sf::Packet* p_packet)
{
	int 					ms_code, ms_id;
	float 					ms_angle;

	//Debug::printTrace(Debug::information,"Network/Client.NotifyNewShoot", "");

	(*p_packet)	>> ms_code
				>> ms_id
				>> ms_angle;

	this->m_gameEngine->Shoot(this->m_gameEngine->GetPlayerById(ms_id), ms_angle);
}
/**
 * Be notified by the server that a player has been hit
 */
void Client::NotifyPlayerHit(sf::Packet* p_packet)
{
	int 					ms_code, ms_id, ms_state;
	float 					ms_life;

	Debug::printTrace(Debug::information,"Network/Client.NotifyPlayerHit", "");

	(*p_packet)	>> ms_code
				>> ms_id
				>> ms_life
				>> ms_state;

	Character* 	l_player = this->m_gameEngine->GetPlayerById(ms_id);
				l_player->SetLife(ms_life);
				l_player->SetState(ms_state);
}
/**
 * Be notified by the server that a player a player score changed
 */
void Client::NotifyPlayerDeath(sf::Packet* p_packet)
{
	int 					ms_code, ms_id;
	unsigned int			ms_death;

	Debug::printTrace(Debug::information,"Network/Client.NotifyPlayerDeath", "");

	(*p_packet)	>> ms_code
				>> ms_id
				>> ms_death;

	Character* 	l_player = this->m_gameEngine->GetPlayerById(ms_id);
				l_player->SetDeath(ms_death);
}
/**
 * Be notified by the server that a player a player score changed
 */
void Client::NotifyPlayerKill(sf::Packet* p_packet)
{
	int 					ms_code, ms_id;
	unsigned int			ms_kill;

	Debug::printTrace(Debug::information,"Network/Client.NotifyPlayerDeath", "");

	(*p_packet)	>> ms_code
				>> ms_id
				>> ms_kill;

	Character* 	l_player = this->m_gameEngine->GetPlayerById(ms_id);
				l_player->SetKill(ms_kill);
}

/**
 * Be notified by the server that a player leave the game
 */
void Client::NotifyPlayerLeave(sf::Packet* p_packet)
{
	int 					ms_code, ms_id;

	Debug::printTrace(Debug::information,"Network/Client.NotifyPlayerLeave", "");

	(*p_packet)	>> ms_code
				>> ms_id;

	this->m_gameEngine->RemovePlayer(this->m_gameEngine->GetPlayerById(ms_id));
}

